package net.gdface.bean;

import java.beans.BeanInfo;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Throwables;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
import com.google.common.primitives.Primitives;

import net.gdface.bean.descriptor.FieldPropertyDescriptor;
import net.gdface.bean.descriptor.LenientDecoratorOfDescriptor;
import net.gdface.bean.descriptor.MappedPropertyDescriptor;
import net.gdface.bean.descriptor.NoStandardPropertyDescriptor;
import net.gdface.bean.exception.BeanPropertyRuntimeException;
import net.gdface.bean.exception.CycleReferenceException;
import net.gdface.bean.exception.NestedNullException;
import net.gdface.bean.factory.PropertyDescriptorFactories;
import net.gdface.bean.factory.PropertyDescriptorFactory;
import net.gdface.logger.SimpleLogger;
import net.gdface.reflection.MethodUtils;
import static java.util.Objects.deepEquals;
import static net.gdface.json.JsonSupports.jsonSupportInstance;
import static net.gdface.utils.CaseSupport.toCamelcase;
import static net.gdface.utils.CaseSupport.isSnakecase;

/**
 * 参照 common-beanutils org.apache.commons.beanutils.PropertyUtilsBean 实现 Java Bean 的多级嵌套读写，
 * 扩展支持Map,类成员，JSON String(需要JSON库[fastjson or jackson]支持) ,并提供自定义 PropertyDescriptor 支持
 * @author guyadong
 * @since 2.7.0
 */
public class BeanPropertySupport {
	private static final Logger logger = SimpleLogger.getLogger(BeanPropertySupport.class.getSimpleName());
	public static final BeanPropertySupport BEAN_SUPPORT = new BeanPropertySupport();
	private final LoadingCache<Class<?>, BeanIntrospectionData> descriptorsCache = CacheBuilder.newBuilder()
			.expireAfterAccess(1, TimeUnit.HOURS)
			.build(new CacheLoader<Class<?>, BeanIntrospectionData>(){

				@Override
				public BeanIntrospectionData load(Class<?> key) throws Exception {
					return getIntrospectionData0(key);
				}});
	private final Cache<String, Map<String, PropertyDescriptor>> mappedDescriptorsCache = CacheBuilder.newBuilder()
			.expireAfterAccess(1, TimeUnit.HOURS)
			.build();
	private final NameResolver resolver = new NameResolver();
	
	private final PropertyDescriptorFactories propertyDescriptorFactories = new PropertyDescriptorFactories();

    /** An empty object array */
	private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];

	protected BeanPropertySupport() {
	}

	private BeanIntrospectionData  getIntrospectionData0(Class<?> beanClass) throws IntrospectionException {
		BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);

        PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
        if (descriptors == null) {
            descriptors = new PropertyDescriptor[0];
        }
        LinkedList<PropertyDescriptor> descriptorList = new LinkedList<>(Arrays.asList(descriptors));
        LinkedList<Field> fields = new LinkedList<>(Arrays.asList(beanClass.getFields()));
        for(Iterator<Field> itor = fields.iterator();itor.hasNext();){
        	Field next = itor.next();
        	if(Modifier.isStatic(next.getModifiers())){
        		itor.remove();
        		continue;
        	}
        	for(PropertyDescriptor pd:descriptorList){
        		if(pd.getName().equals(next.getName())){
        			if(!pd.getPropertyType().equals(next.getType())){
        				throw new IntrospectionException("FIND Field with save name: "+ next.getName() +",but mismatch Type " + next.getType());          			
            		}
        			itor.remove();
        			break;
        		}
        	}
        }
        for(Field field:fields){
        	descriptorList.add(new FieldPropertyDescriptor(field));
        }
        return new BeanIntrospectionData(descriptorList.toArray(new PropertyDescriptor[descriptorList.size()]));
	}
	/**
	 * Obtains the {@code BeanIntrospectionData} object describing the specified bean
	 * class. This object is looked up in the internal cache. If necessary, introspection
	 * is performed now on the affected bean class, and the results object is created.
	 *
	 * @param beanClass the bean class in question
	 * @return the {@code BeanIntrospectionData} object for this class
	 * @throws IllegalArgumentException if the bean class is <b>null</b>
	 */
	private BeanIntrospectionData getIntrospectionData(Class<?> beanClass) {
	    if (beanClass == null) {
	        throw new IllegalArgumentException("No bean class specified");
	    }
		try {
			return descriptorsCache.get(beanClass);
		} catch (ExecutionException e) {
			throw new BeanPropertyRuntimeException(e.getCause());
		}	
	}

	public PropertyDescriptor[]  getPropertyDescriptors(Class<?> beanClass) {
		return getIntrospectionData(beanClass).getDescriptors();
	}
	/**
	 * 获取beanClass中所有具有指定读写类型(rw)的属性
	 * @param beanClass
	 * @param rw 属性类型标记 <br>
	 * 					<li>0 所有属性</li>
	 * 					<li>1 读属性</li>
	 * 					<li>2 写属性</li>
	 * 					<li>3 读写属性</li>
	 * @param lenient 是否为宽容模式---允许返回类型不为void的setter方法
	 * @return 属性名与PropertyDescriptor映射的Map对象
	 */
	private HashMap<String, PropertyDescriptor> getProperties0(Class<?> beanClass, int rw,boolean lenient) {
		HashMap<String, PropertyDescriptor> properties = new HashMap<String, PropertyDescriptor>();
		if (beanClass != null) {
			PropertyDescriptor[] origDescriptors = getPropertyDescriptors(beanClass);
			Boolean put;
			for (PropertyDescriptor pd : origDescriptors) {
				if(lenient && !(pd instanceof FieldPropertyDescriptor)){
					pd = LenientDecoratorOfDescriptor.toDecorator(pd);
				}
				put = false;
				switch (rw &= 3) {
				case 0:
					put = hasReadMethod(pd) || hasWriteMethod(pd);
					break;
				case 1:
					put = hasWriteMethod(pd);
					break;
				case 2:
					put = hasReadMethod(pd);
					break;
				case 3:
					put = hasReadMethod(pd) && hasWriteMethod(pd);
					break;
				}
				if (put) {
					properties.put(pd.getName(), pd);
				}
			}
		}
		return properties;
	}
	/**
	 * 获取beanClass中所有具有指定读写类型(rw)的属性
	 * @param beanClass
	 * @param rw 属性类型标记 <br>
	 * 					<li>0 所有属性</li>
	 * 					<li>1 读属性</li>
	 * 					<li>2 写属性</li>
	 * 					<li>3 读写属性</li>
	 * @param lenient 是否为宽容模式---允许返回类型不为void的setter方法
	 * @return 属性名与PropertyDescriptor映射的Map对象
	 */
	public Map<String, PropertyDescriptor> getProperties(final Class<?> beanClass, final int rw,final boolean lenient) {
		if(null != beanClass){
			try {
				String key = String.format("%s_%d_%b", beanClass.getName(),rw,lenient); 
				return mappedDescriptorsCache.get(
						key, 
						new Callable<Map<String,PropertyDescriptor>>() {
							@Override
							public Map<String, PropertyDescriptor> call() throws Exception {
								return getProperties0(beanClass,rw,lenient);
							}
						});
			} catch (ExecutionException e) {
				throw new BeanPropertyRuntimeException(e.getCause());
			}
		}
		return Collections.emptyMap();
	}
	/**
	 * <p>Return the mapped property descriptors for this bean class.</p>
	 * @see #getProperties(Class, int, boolean)
	 * @param beanClass
	 */
	public Map<String, PropertyDescriptor> getMappedPropertyDescriptors(final Class<?> beanClass) {
		return getProperties(beanClass, 3, true);
	}
    /**
	 * <p>Return the mapped property descriptors for this bean.</p>
	 *
	 * @param bean Bean to be introspected
	 * @return the mapped property descriptors
	 */
	public Map<String, PropertyDescriptor> getMappedPropertyDescriptors(Object bean) {
	
	    if (bean == null) {
	        return null;
	    }
	    return getMappedPropertyDescriptors(bean.getClass());
	
	}
	/**
     * Clear any cached property descriptors information for all classes
     * loaded by any class loaders.  This is useful in cases where class
     * loaders are thrown away to implement class reloading.
     */
    public void clearDescriptors() {
    	descriptorsCache.asMap().clear();
    	mappedDescriptorsCache.asMap().clear();
        Introspector.flushCaches();

    }
    /**
	 * <p>Retrieve the property descriptors for the specified bean,
	 * introspecting and caching them the first time a particular bean class
	 * is encountered.</p>
	 *
	 * @param bean Bean for which property descriptors are requested
	 * @return the property descriptors
	 *
	 * @exception IllegalArgumentException if <code>bean</code> is null
	 */
	public PropertyDescriptor[] getPropertyDescriptors(Object bean) {
	
	    if (bean == null) {
	        throw new IllegalArgumentException("No bean specified");
	    }
	    return (getPropertyDescriptors(bean.getClass()));
	
	}

	/**
     * <p>Retrieve the property descriptor for the specified property of the
     * specified bean, or return <code>null</code> if there is no such
     * descriptor.  This method resolves indexed and nested property
     * references in the same manner as other methods in this class, except
     * that if the last (or only) name element is indexed, the descriptor
     * for the last resolved property itself is returned.</p>
     *
     * @param bean Bean for which a property descriptor is requested
     * @param name Possibly indexed and/or nested name of the property for
     *  which a property descriptor is requested
     * @return the property descriptor
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception IllegalArgumentException if a nested reference to a
     *  property returns null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    public PropertyDescriptor getPropertyDescriptor(Object bean,
                                                           String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Resolve nested references
        while (resolver.hasNested(name)) {
            String next = resolver.next(name);
            Object nestedBean = getPropertyChecked(bean, next);
            if (nestedBean == null) {
                throw new NestedNullException
                        ("Null property value for '" + next +
                        "' on bean class '" + bean.getClass() + "'");
            }
            bean = nestedBean;
            name = resolver.remove(name);
        }

        // Remove any subscript from the final name value
        name = resolver.getProperty(name);

        // Look up and return this property from our cache
        // creating and adding it to the cache if not found.
        if (name == null) {
            return (null);
        }
        PropertyDescriptor result;
        try {
        	/** 优先获取非标准 Descriptor  */
        	result = propertyDescriptorFactories.nostandardDescriptorOf(bean,name);
        	if (result != null) {
        		return result;
        	}
		} catch (IntrospectionException e) {
		}
        /** 获取标准 Descriptor  */
        result = getIntrospectionData(bean.getClass()).getDescriptor(name);
        if (result != null) {
            return result;
        }
        Map<String, PropertyDescriptor> mappedDescriptors =
                getMappedPropertyDescriptors(bean);

        result = mappedDescriptors.get(name);
        if (result == null) {
            // not found, try to create it
            try {
            	/** 获取内置 Descriptor  */
            	result= propertyDescriptorFactories.builtinDescriptorOf(bean, name);
				if(null == result){
					result = new MappedPropertyDescriptor(name, bean.getClass());
				}
            } catch (IntrospectionException ie) {
                /* Swallow IntrospectionException
                 * TODO: Why?
                 */
            }
            if (result != null ) {
            	if(!(bean instanceof Map || bean instanceof Iterable)){
            		/** 动态对象不需要缓存 PropertyDescriptor 实例 */
            		mappedDescriptors.put(name, result);
            	}
            }
        }

        return result;

    }
    /**
     * Return the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     * 
     * @see #getProperty(Object, String, Object)
     * @param bean Bean whose property is to be extracted
     * @param name Possibly nested name of the property to be extracted
     * @return the nested property value
     * 
     */
    public Object getProperty(Object bean, String name){
    	return getProperty(bean,name,null);
    }
    /**
     * Return the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Possibly nested name of the property to be extracted
     * @param defaultValue default value if value of property is null
     * @return the nested property value
     * @throws BeanPropertyRuntimeException wrap all reflect option exception 
     * 
     */
    public Object getProperty(Object bean, String name,Object defaultValue)
    		throws BeanPropertyRuntimeException {
    	try {
    		Object value = getPropertyChecked(bean,name);
    		if(null == value){
    			return defaultValue;
    		}
    		if(null != defaultValue && !defaultValue.getClass().isInstance(value) && maybeJsonString(value) > 0){
    			/** 尝试解析为对象,解析失败则忽略返回原始值 */
   				return jsonSupportInstance().parseOrEmptyMap((String)value,false);
    		}
    		return value;
    	} catch (NoSuchMethodException e) {
    		return defaultValue;
    	}catch (ReflectiveOperationException e) {
    		throw new BeanPropertyRuntimeException(e);
    	}catch (IndexOutOfBoundsException e) {
    		return defaultValue;
		}
    }
    /**
     * Return the value of the (possibly nested) property of the specified
     * name, for the specified bean, cast to target type.if value is String,try to parse json to target type.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Possibly nested name of the property to be extracted
     * @param targetType type required
     * @return the nested property value
     * @throws BeanPropertyRuntimeException wrap all reflect option exception 
     * 
     */
    public <T>T getPropertyCheckType(Object bean, String name,Class<T> targetType)
    		throws BeanPropertyRuntimeException {
    	try {
    		Object value = getPropertyChecked(bean,name);
    		if(null == value){
    			return null;
    		}
    		if(null == targetType){
    			throw new NullPointerException("targetType is null");
    		}
    		try {
    			return targetType.cast(value);
			} catch (ClassCastException e) {
				if(value instanceof String){
					/** 尝试解析为对象 */
					return jsonSupportInstance().parse((String)value,targetType);
				}
				throw e;
			}
    	} catch (NoSuchMethodException e) {
    		return null;
    	}catch (ReflectiveOperationException e) {
    		throw new BeanPropertyRuntimeException(e);
    	}
    }
    /**
     * Return the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Possibly nested name of the property to be extracted
     * @return the nested property value
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception NestedNullException if a nested reference to a
     *  property returns null
     * @exception InvocationTargetException
     * if the property accessor method throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    public Object getPropertyChecked(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
        return new NestedContext(bean,name, false).visit(new ReadVisitor());

    }

    private Object getProperty0(Object bean, String name) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
        if (resolver.isMapped(name)) {
            bean = getMappedProperty(bean, name);
        } else if (resolver.isIndexed(name)) {
            bean = getIndexedProperty(bean, name);
        } else if (resolver.isSearched(name)) {
            bean = getSearchedProperty(bean, name);
        }else {
            bean = getSimpleProperty(bean, name);
        }
        return bean;
    }
    /**
     * Return the value of the specified mapped property of the
     * specified bean, with no type conversions.  The key of the
     * required value must be included (in brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.
     *
     * @param bean Bean whose property is to be extracted
     * @param name <code>propertyname(key)</code> of the property value
     *  to be extracted
     * @return the mapped property value
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected Object getMappedProperty(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the key of the requested individual property
        String key  = null;
        try {
            key = resolver.getKey(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException
                    ("Invalid mapped property '" + name +
                    "' on bean class '" + bean.getClass() + "' " + e.getMessage());
        }
        if (key == null) {
            throw new IllegalArgumentException("Invalid mapped property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);

        // Request the specified indexed property value
        return (getMappedProperty(bean, name, key));

    }
    /**
     * Return the value of the specified mapped property of the specified
     * bean, with no type conversions.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Mapped property name of the property value to be extracted
     * @param key Key of the property value to be extracted
     * @return the mapped property value
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected Object getMappedProperty(Object bean,
                                           String name, String key)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }
        if (key == null) {
            throw new IllegalArgumentException("No key specified for property '" +
                    name + "' on bean class " + bean.getClass() + "'");
        }

        Object result = null;

        // Retrieve the property descriptor for the specified property
        PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
        if (descriptor == null) {
            throw new NoSuchMethodException("Unknown property '" +
                    name + "'+ on bean class '" + bean.getClass() + "'");
        }

        if (descriptor instanceof MappedPropertyDescriptor) {
            // Call the keyed getter method if there is one
            Method readMethod = ((MappedPropertyDescriptor) descriptor).
                    getMappedReadMethod();
            readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
            if (readMethod != null) {
                Object[] keyArray = new Object[1];
                keyArray[0] = key;
                result = invokeMethod(descriptor,readMethod, bean, keyArray);
            } else {
                throw new NoSuchMethodException("Property '" + name +
                        "' has no mapped getter method on bean class '" +
                        bean.getClass() + "'");
            }
        } else {
          /* means that the result has to be retrieved from a map */
          Method readMethod = getReadMethod(bean.getClass(), descriptor);
          if (readMethod != null) {
            Object invokeResult = invokeMethod(descriptor,readMethod, bean, EMPTY_OBJECT_ARRAY);
            /* test and fetch from the map */
            if (invokeResult instanceof java.util.Map) {
              result = ((java.util.Map<?, ?>)invokeResult).get(key);
            }
          } else {
            throw new NoSuchMethodException("Property '" + name +
                    "' has no mapped getter method on bean class '" +
                    bean.getClass() + "'");
          }
        }
        return result;

    }
    /**
     * Return the value of the specified indexed property of the specified
     * bean, with no type conversions.  The zero-relative index of the
     * required value must be included (in square brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.  In addition to supporting the JavaBeans specification, this
     * method has been extended to support <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be extracted
     * @param name <code>propertyname[index]</code> of the property value
     *  to be extracted
     * @return the indexed property value
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying array or List
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected Object getIndexedProperty(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the index of the requested individual property
        int index;
        try {
            index = resolver.getIndex(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "' " +
                    e.getMessage());
        }
        if (index < -2) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);

        // Request the specified indexed property value
        return (getIndexedProperty(bean, name, index));

    }


    /**
     * Return the value of the specified indexed property of the specified
     * bean, with no type conversions.  In addition to supporting the JavaBeans
     * specification, this method has been extended to support
     * <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Simple property name of the property value to be extracted
     * @param index Index of the property value to be extracted
     * @return the indexed property value
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying property
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected Object getIndexedProperty(Object bean,
                                            String name, int index)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null || name.length() == 0) {
            return getIndexValue("", bean,index);
        }

        // Retrieve the property descriptor for the specified property
        PropertyDescriptor descriptor =
                getPropertyDescriptor(bean, name);
        if (descriptor == null) {
            throw new NoSuchMethodException("Unknown property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Call the indexed getter method if there is one
        if (descriptor instanceof IndexedPropertyDescriptor) {
            Method readMethod = ((IndexedPropertyDescriptor) descriptor).
                    getIndexedReadMethod();
            readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
            if (readMethod != null) {
                Object[] subscript = new Object[1];
                /** index为 -1 时视为获取头部第一个元素 */
                subscript[0] = new Integer(-1 == index ? 0 : index);
                try {
                    return (invokeMethod(descriptor,readMethod,bean, subscript));
                } catch (InvocationTargetException e) {
                    if (e.getTargetException() instanceof
                            IndexOutOfBoundsException) {
                        throw (IndexOutOfBoundsException)
                                e.getTargetException();
                    } else {
                        throw e;
                    }
                }
            }
        }

        // Otherwise, the underlying property must be an array
        Method readMethod = getReadMethod(bean.getClass(), descriptor);
        if (readMethod == null) {
            throw new NoSuchMethodException("Property '" + name + "' has no " +
                    "getter method on bean class '" + bean.getClass() + "'");
        }

        // Call the property getter and return the value
        Object value = invokeMethod(descriptor,readMethod, bean, EMPTY_OBJECT_ARRAY);
        return getIndexValue(name,value,index);

    }
    /**
     * Return the value of the specified indexed property of the specified
     * bean, with no type conversions.  The zero-relative index of the
     * required value must be included (in square brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.  In addition to supporting the JavaBeans specification, this
     * method has been extended to support <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be extracted
     * @param name <code>propertyname[index]</code> of the property value
     *  to be extracted
     * @return the indexed property value
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying array or List
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected Object getSearchedProperty(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the index of the requested individual property
        String field = null;
        String conditionValue = null;
        try {
            field = resolver.getField(name);
            conditionValue = resolver.getValue(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "' " +
                    e.getMessage());
        }
        if (null == field || 0 == field.length() || null == conditionValue || 0 == conditionValue.length()) {
            throw new IllegalArgumentException("Invalid search property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);

        // Request the specified indexed property value
        return (getSearchedProperty(bean, name, field,conditionValue));

    }
    /**
     * Return the value of the specified indexed property of the specified
     * bean, with no type conversions.  In addition to supporting the JavaBeans
     * specification, this method has been extended to support
     * <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Simple property name of the property value to be extracted
     * @param field sub property name
     * @param conditionValue search satisfied value of field  
     * @return the indexed property value
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying property
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  property cannot be found
     */
    protected Object getSearchedProperty(Object bean,
    		String name,String field,Object conditionValue)
    				throws IllegalAccessException, InvocationTargetException,
    				NoSuchMethodException {
    	
    	if (bean == null) {
    		throw new IllegalArgumentException("No bean specified");
    	}
    	if (name == null || name.length() == 0) {
    		return searchIn(bean,field,conditionValue);
    	}
    	
    	// Retrieve the property descriptor for the specified property
    	PropertyDescriptor descriptor =
    			getPropertyDescriptor(bean, name);
    	if (descriptor == null) {
    		throw new NoSuchMethodException("Unknown property '" +
    				name + "' on bean class '" + bean.getClass() + "'");
    	}
    	
    	// Call the indexed getter method if there is one
    	if (descriptor instanceof IndexedPropertyDescriptor) {
    		throw new UnsupportedOperationException("UNSUPPORT IndexedPropertyDescriptor for search property");
    	}
    	
    	// Otherwise, the underlying property must be an array
    	Method readMethod = getReadMethod(bean.getClass(), descriptor);
    	if (readMethod == null) {
    		throw new NoSuchMethodException("Property '" + name + "' has no " +
    				"getter method on bean class '" + bean.getClass() + "'");
    	}
    	
    	// Call the property getter and return the value
    	Object value = invokeMethod(descriptor,readMethod, bean, EMPTY_OBJECT_ARRAY);
    	return searchIn(value,field,conditionValue);    	
    }
    /**
     * Return the value of the specified simple property of the specified
     * bean, with no type conversions.
     *
     * @param bean Bean whose property is to be extracted
     * @param name Name of the property to be extracted
     * @return The property value
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception IllegalArgumentException if the property name
     *  is nested or indexed
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected Object getSimpleProperty(Object bean, String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Validate the syntax of the property name
        if (resolver.hasNested(name)) {
            throw new IllegalArgumentException
                    ("Nested property names are not allowed: Property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        } else if (resolver.isIndexed(name)) {
            throw new IllegalArgumentException
                    ("Indexed property names are not allowed: Property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        } else if (resolver.isMapped(name)) {
            throw new IllegalArgumentException
                    ("Mapped property names are not allowed: Property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }else if (resolver.isSearched(name)) {
            throw new IllegalArgumentException
            ("Search property names are not allowed: Property '" +
            name + "' on bean class '" + bean.getClass() + "'");
}

        // Retrieve the property getter method for the specified property
        PropertyDescriptor descriptor =
                getPropertyDescriptor(bean, name);
        if (descriptor == null) {
            throw new NoSuchMethodException("Unknown property '" +
                    name + "' on class '" + bean.getClass() + "'" );
        }
        Method readMethod = getReadMethod(bean.getClass(), descriptor);
        if (readMethod == null) {
            throw new NoSuchMethodException("Property '" + name +
                    "' has no getter method in class '" + bean.getClass() + "'");
        }
       	return  invokeMethod(descriptor,readMethod, bean, EMPTY_OBJECT_ARRAY);

    }
    /**
     * Set the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     * @see #setPropertyChecked(Object, String, Object)
     * @param bean Bean whose property is to be modified
     * @param name Possibly nested name of the property to be modified
     * @param value Value to which the property is to be set
     * @return 
     *  @throws BeanPropertyRuntimeException wrap all reflect option exception
     */
    public Object setProperty(Object bean,String name, Object value){
    	try {
			return setPropertyChecked(bean,name,value);
		} catch (NoSuchMethodException e) {
			// DO NOTHING
			return bean;
		}catch (ReflectiveOperationException e) {
			throw new BeanPropertyRuntimeException(e);
		}
    }
    
    /**
     * Set the value of the (possibly nested) property of the specified
     * name, for the specified bean, with no type conversions.
     * <p>
     * Example values for parameter "name" are:
     * <ul>
     * <li> "a" -- sets the value of property a of the specified bean </li>
     * <li> "a.b" -- gets the value of property a of the specified bean,
     * then on that object sets the value of property b.</li>
     * <li> "a(key)" -- sets a value of mapped-property a on the specified
     * bean. This effectively means bean.setA("key").</li>
     * <li> "a[3]" -- sets a value of indexed-property a on the specified
     * bean. This effectively means bean.setA(3).</li>
     * </ul>
     *
     * @param bean Bean whose property is to be modified
     * @param name Possibly nested name of the property to be modified
     * @param value Value to which the property is to be set
     * @return 
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception IllegalArgumentException if a nested reference to a
     *  property returns null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    public Object setPropertyChecked(Object bean,
                                         String name, Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
    	return new NestedContext(bean, name, value).visit(new WriteVisitor());
    }
    private Object setProperty0(Object bean,
            String name, Object value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{

    	if (resolver.isMapped(name)) {
    		setMappedProperty(bean, name, value);
    	} else if (resolver.isIndexed(name)) {
    		setIndexedProperty(bean, name, value);
    	} else {
    		setSimpleProperty(bean, name, value);
    	}
    	return bean;
    }
    
    /**
     * Set the value of the specified mapped property of the
     * specified bean, with no type conversions.  The key of the
     * value to set must be included (in brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.
     *
     * @param bean Bean whose property is to be set
     * @param name <code>propertyname(key)</code> of the property value
     *  to be set
     * @param value The property value to be set
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected void setMappedProperty(Object bean, String name,
                                         Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the key of the requested individual property
        String key  = null;
        try {
            key = resolver.getKey(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException
                    ("Invalid mapped property '" + name +
                    "' on bean class '" + bean.getClass() + "'");
        }
        if (key == null) {
            throw new IllegalArgumentException
                    ("Invalid mapped property '" + name +
                    "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);

        // Request the specified indexed property value
        setMappedProperty(bean, name, key, value);

    }


    /**
     * Set the value of the specified mapped property of the specified
     * bean, with no type conversions.
     *
     * @param bean Bean whose property is to be set
     * @param name Mapped property name of the property value to be set
     * @param key Key of the property value to be set
     * @param value The property value to be set
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    @SuppressWarnings("unchecked")
    protected void setMappedProperty(Object bean, String name,
                                         String key, Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }
        if (key == null) {
            throw new IllegalArgumentException("No key specified for property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Retrieve the property descriptor for the specified property
        PropertyDescriptor descriptor =
                getPropertyDescriptor(bean, name);
        if (descriptor == null) {
            throw new NoSuchMethodException("Unknown property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        if (descriptor instanceof MappedPropertyDescriptor) {
            // Call the keyed setter method if there is one
            Method mappedWriteMethod =
                    ((MappedPropertyDescriptor) descriptor).
                    getMappedWriteMethod();
            mappedWriteMethod = MethodUtils.getAccessibleMethod(bean.getClass(), mappedWriteMethod);
            if (mappedWriteMethod != null) {
                Object[] params = new Object[2];
                params[0] = key;
                params[1] = value;
                if (traceEnabled) {
                    String valueClassName =
                        value == null ? "<null>" : value.getClass().getName();
                    logger.info("setSimpleProperty: Invoking method "
                              + mappedWriteMethod + " with key=" + key
                              + ", value=" + value
                              + " (class " + valueClassName +")");
                }
                invokeMethod(descriptor,mappedWriteMethod, bean, params);
            } else {
                throw new NoSuchMethodException
                    ("Property '" + name + "' has no mapped setter method" +
                     "on bean class '" + bean.getClass() + "'");
            }
        } else {
          /* means that the result has to be retrieved from a map */
          Method readMethod = getReadMethod(bean.getClass(), descriptor);
          if (readMethod != null) {
            Object invokeResult = invokeMethod(descriptor,readMethod, bean, EMPTY_OBJECT_ARRAY);
            /* test and fetch from the map */
            if (invokeResult instanceof Map) {
            	((Map<String, Object>)invokeResult).put(key, value);
            }
          } else {
            throw new NoSuchMethodException("Property '" + name +
                    "' has no mapped getter method on bean class '" +
                    bean.getClass() + "'");
          }
        }

    }
    /**
     * Set the value of the specified indexed property of the specified
     * bean, with no type conversions.  The zero-relative index of the
     * required value must be included (in square brackets) as a suffix to
     * the property name, or <code>IllegalArgumentException</code> will be
     * thrown.  In addition to supporting the JavaBeans specification, this
     * method has been extended to support <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be modified
     * @param name <code>propertyname[index]</code> of the property value
     *  to be modified
     * @param value Value to which the specified property element
     *  should be set
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying property
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    protected void setIndexedProperty(Object bean, String name,
                                          Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Identify the index of the requested individual property
        int index;
        try {
            index = resolver.getIndex(name);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }
        if (index < -2) {
            throw new IllegalArgumentException("Invalid indexed property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Isolate the name
        name = resolver.getProperty(name);        
        // Set the specified indexed property value
        setIndexedProperty(bean, name, index, value);

    }

    /**
     * Set the value of the specified indexed property of the specified
     * bean, with no type conversions.  In addition to supporting the JavaBeans
     * specification, this method has been extended to support
     * <code>List</code> objects as well.
     *
     * @param bean Bean whose property is to be set
     * @param name Simple property name of the property value to be set
     * @param index Index of the property value to be set
     * @param value Value to which the indexed property element is to be set
     *
     * @exception IndexOutOfBoundsException if the specified index
     *  is outside the valid range for the underlying property
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
	protected void setIndexedProperty(Object bean, String name,
                                          int index, Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null || name.length() == 0) {
        	setOrAddIndexValue(name,bean,index, value);
        	return;
        }
        // Retrieve the property descriptor for the specified property
        PropertyDescriptor descriptor =
                getPropertyDescriptor(bean, name);
        if (descriptor == null) {
            throw new NoSuchMethodException("Unknown property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }

        // Call the indexed setter method if there is one
        if (descriptor instanceof IndexedPropertyDescriptor) {
            Method writeMethod = ((IndexedPropertyDescriptor) descriptor).
                    getIndexedWriteMethod();
            writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
            if (writeMethod != null) {
                Object[] subscript = new Object[2];
                subscript[0] = new Integer(index);
                subscript[1] = value;
                try {
                    if (traceEnabled) {
                        String valueClassName =
                            value == null ? "<null>"
                                          : value.getClass().getName();
                        logger.info("setSimpleProperty: Invoking method "
                                  + writeMethod +" with index=" + index
                                  + ", value=" + value
                                  + " (class " + valueClassName+ ")");
                    }
                    invokeMethod(descriptor,writeMethod, bean, subscript);
                } catch (InvocationTargetException e) {
                	Throwables.throwIfInstanceOf(e.getTargetException(), IndexOutOfBoundsException.class);
                    throw e;
                }
                return;
            }
        }

        // Otherwise, the underlying property must be an array or a list
        Method readMethod = getReadMethod(bean.getClass(), descriptor);
        if (readMethod == null) {
            throw new NoSuchMethodException("Property '" + name +
                    "' has no getter method on bean class '" + bean.getClass() + "'");
        }

        // Call the property getter to get the array or list
        Object array = invokeMethod(descriptor,readMethod, bean, EMPTY_OBJECT_ARRAY);
        if(null ==array){
        	/** 如果字段为null,尝试创建新的List对象 */
        	Class<?> propertyType = descriptor.getPropertyType();
        	if(Object.class==propertyType || null == propertyType){
        		array = new LinkedList<>();
        		 // Otherwise, the underlying property must be an array or a list
                Method writeMethod = getWriteMethod(bean.getClass(), descriptor);
                if (writeMethod == null) {
                    throw new NoSuchMethodException("Property '" + name +
                            "' has no setter method on bean class '" + bean.getClass() + "'");
                }
                invokeMethod(descriptor,writeMethod, bean, new Object[]{array});
        	}
        }
        setOrAddIndexValue(name,array,index, value);

    }
    /**
	 * 设置{@link List}对象索引指定位置的值<br>
	 * index为-1时向列表头部添加元素,
	 * index为-2时向列表尾部添加元素,
	 * @param name 
	 * @param bean
	 * @param index
	 * @param value
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void setOrAddIndexValue(String name,Object bean,int index, Object value){
		if(null == bean){
	    	throw new IllegalArgumentException("Property '" + name + "' is null ");
		}else if(bean.getClass().isArray()){
			switch (index) {
			case -1:
				Array.set(bean, 0, value);
				break;
			default:
				Array.set(bean, index, value);
				break;
			}
		}else if(bean instanceof List){
			List list = (List)bean;
			switch (index) {
			case -1:
				/** 添加到列表头部 */
				list.add(0, value);
				break;
			case -2:
				/** 添加到列表尾部 */
				list.add(value);
				break;
			default:
				list.set(index, value);
				break;
			}	
		}else{
			throw new IllegalArgumentException("Property '" + name +
					"' is not indexed on bean class '" + bean.getClass() + "'");
		}
	}

	/**
	 * 返回{@link List}对象索引指定位置的值<br>
	 * index为-1时返回列表第一个元素，等同于0,
	 * index为-2时返回列表最后一个元素,
	 * @param bean
	 * @param index
	 * @param bean
	 */
	private Object getIndexValue(String name,Object bean,int index){
		if(null == bean){
	    	return null;
		}else if(bean.getClass().isArray()){
			try {
				switch (index) {
				case -1:
					/** 添加到列表头部 */
					return Array.get(bean,0);
				case -2:
					/** 返回列表最后一个元素 */
					return Array.get(bean,Array.getLength(bean)-1);
				default:
					return Array.get(bean,index);
				}	    		
				
			} catch (ArrayIndexOutOfBoundsException e) {
	            throw new ArrayIndexOutOfBoundsException("Index: " +
	                    index + ", Size: " + Array.getLength(bean) +
	                    " for property '" + name + "'");
	        }
			
		}else if(bean instanceof List){
			List<?> list = (List<?>)bean;
			switch (index) {
			case -1:
				/** 添加到列表头部 */
				return list.get(0);
			case -2:
				/** 返回列表最后一个元素 */
				return list.get(list.size()-1);
			default:
				return list.get(index);
			}	
		}else {
			throw new IllegalArgumentException("Property '" + name +
	                "' is not indexed on bean class '" + bean.getClass() + "'");
		}
	}

	/**
     * Set the value of the specified simple property of the specified bean,
     * with no type conversions.
     *
     * @param bean Bean whose property is to be modified
     * @param name Name of the property to be modified
     * @param value Value to which the property should be set
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if <code>bean</code> or
     *  <code>name</code> is null
     * @exception IllegalArgumentException if the property name is
     *  nested or indexed
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected void setSimpleProperty(Object bean,
                                         String name, Object value)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (bean == null) {
            throw new IllegalArgumentException("No bean specified");
        }
        if (name == null) {
            throw new IllegalArgumentException("No name specified for bean class '" +
                    bean.getClass() + "'");
        }

        // Validate the syntax of the property name
        if (resolver.hasNested(name)) {
            throw new IllegalArgumentException
                    ("Nested property names are not allowed: Property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        } else if (resolver.isIndexed(name)) {
            throw new IllegalArgumentException
                    ("Indexed property names are not allowed: Property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        } else if (resolver.isMapped(name)) {
            throw new IllegalArgumentException
                    ("Mapped property names are not allowed: Property '" +
                    name + "' on bean class '" + bean.getClass() + "'");
        }else if (resolver.isSearched(name)) {
            throw new IllegalArgumentException
            ("Searched property names are not allowed: Property '" +
            name + "' on bean class '" + bean.getClass() + "'");
        }

        // Retrieve the property setter method for the specified property
        PropertyDescriptor descriptor =
                getPropertyDescriptor(bean, name);
        if (descriptor == null) {
            throw new NoSuchMethodException("Unknown property '" +
                    name + "' on class '" + bean.getClass() + "'" );
        }
        Method writeMethod = getWriteMethod(bean.getClass(), descriptor);
        if (writeMethod == null) {
            throw new NoSuchMethodException("Property '" + name +
                    "' has no setter method in class '" + bean.getClass() + "'");
        }

        // Call the property setter method
        if (traceEnabled) {
            String valueClassName =
                value == null ? "<null>" : value.getClass().getName();
            logger.info("setSimpleProperty: Invoking method " + writeMethod
                      + " with value " + value + " (class " + valueClassName + ")");
        }
        {
        	/** 当value 与目标类型不同时的处理 */
        	Class<?> propertyType = descriptor.getPropertyType();
        	if(null == value){
        		// DO NOTHING
        	}else if(!propertyType.isInstance(value)){
        		if(String.class.equals(propertyType)){        			
        			Object old = getProperty0(bean, name);
        			Map<?,?> oldjson = new LinkedHashMap<>();
        			if(maybeJsonString(old) > 0){
        				Object parsed = jsonSupportInstance().parseOrEmptyMap((String) old,false);
        				if(parsed instanceof Map){
							oldjson = (Map<?,?>) parsed;
        				}
        			}
        			/** 合并value到原数据 */
        			copyProperties(oldjson, value);
        			/** value to json string */
        			value = jsonSupportInstance().toJSONString(oldjson);
        		}else if(value instanceof String){
					 value = jsonSupportInstance().parse((String)value, propertyType);
				}else if (Map.class.isInstance(value) && Map.class.isAssignableFrom(propertyType)) {
	        		Object old = getProperty0(bean, name);
	        		/** 合并value到原数据 */
	        		if(null != old){
	        			((Map) old).putAll((Map)value);
	        			value = old;
	        		}
				}else {
					// DO NOTHING(听天由命)
				}
        	}else if(String.class.equals(propertyType)){
        		Object old = getProperty0(bean, name);
        		if(maybeJsonString(old)>0 && maybeJsonString(value)>0){
        			Object oldparsed = jsonSupportInstance().parseOrEmptyMap((String) old);
        			Object valueparsed = jsonSupportInstance().parseOrEmptyMap((String) value);
        			if(oldparsed instanceof Map && valueparsed instanceof Map){
        				/** 合并value到原数据 */
        				((Map) oldparsed).putAll((Map)valueparsed);
        				value = jsonSupportInstance().toJSONString(oldparsed);
        			}
        		}
        	}else if (Map.class.isAssignableFrom(propertyType)) {
        		Object old = getProperty0(bean, name);
        		if(null != old){
        			/** 合并value到原数据 */
        			copyProperties(old, value);
        			value = old;
        		}
			}
        }
        invokeMethod(descriptor,writeMethod, bean, new Object[]{value});

    }
    /**
	 * <p>Return an accessible property getter method for this property,
	 * if there is one; otherwise return <code>null</code>.</p>
	 *
	 * @param clazz The class of the read method will be invoked on
	 * @param descriptor Property descriptor to return a getter for
	 * @return The read method
	 */
	protected Method getReadMethod(Class<?> clazz, PropertyDescriptor descriptor) {
		if(descriptor instanceof NoStandardPropertyDescriptor){
			return ((NoStandardPropertyDescriptor)descriptor).getReadMethod(clazz, descriptor);
		}
	    return (MethodUtils.getAccessibleMethod(clazz, descriptor.getReadMethod()));
	}

	/**
     * <p>Return an accessible property setter method for this property,
     * if there is one; otherwise return <code>null</code>.</p>
     *
     * @param clazz The class of the read method will be invoked on
     * @param descriptor Property descriptor to return a setter for
     * @return The write method
     */
    protected Method getWriteMethod(Class<?> clazz, PropertyDescriptor descriptor) {
    	if(descriptor instanceof NoStandardPropertyDescriptor){
    		return ((NoStandardPropertyDescriptor)descriptor).getWriteMethod(clazz, descriptor);
    	}
        BeanIntrospectionData data = getIntrospectionData(clazz);
        return (MethodUtils.getAccessibleMethod(clazz,
                data.getWriteMethod(clazz, descriptor)));
    }
    /**
     * <p>Copy property values from the "origin" bean to the "destination" bean
     * for all cases where the property names are the same (even though the
     * actual getter and setter methods might have been customized via
     * <code>BeanInfo</code> classes).  No conversions are performed on the
     * actual property values -- it is assumed that the values retrieved from
     * the origin bean are assignment-compatible with the types expected by
     * the destination bean.</p>
     *
     * <p>If the origin "bean" is actually a <code>Map</code>, it is assumed
     * to contain String-valued <strong>simple</strong> property names as the keys, pointing
     * at the corresponding property values that will be set in the destination
     * bean.<strong>Note</strong> that this method is intended to perform
     * a "shallow copy" of the properties and so complex properties
     * (for example, nested ones) will not be copied.</p>
     *
     * <p>Note, that this method will not copy a List to a List, or an Object[]
     * to an Object[]. It's specifically for copying JavaBean properties. </p>
     *
     * @param dest Destination bean whose properties are modified
     * @param orig Origin bean whose properties are retrieved
     *
     * @exception IllegalAccessException if the caller does not have
     *  access to the property accessor method
     * @exception IllegalArgumentException if the <code>dest</code> or
     *  <code>orig</code> argument is null
     * @exception InvocationTargetException if the property accessor method
     *  throws an exception
     * @exception NoSuchMethodException if an accessor method for this
     *  propety cannot be found
     */
    public void copyProperties(Object dest, Object orig)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {

        if (dest == null) {
            throw new IllegalArgumentException
                    ("No destination bean specified");
        }
        if (orig == null) {
            throw new IllegalArgumentException("No origin bean specified");
        }

        if (orig instanceof Map) {
            Iterator<?> entries = ((Map<?, ?>) orig).entrySet().iterator();
            while (entries.hasNext()) {
                Map.Entry<?, ?> entry = (Entry<?, ?>) entries.next();
                String name = (String)entry.getKey();
                if (isWriteable(dest, name)) {
                    try {
                            setSimpleProperty(dest, name, entry.getValue());
                    } catch (NoSuchMethodException e) {
                        if (traceEnabled) {
                        	logger.log(Level.INFO, "Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
                        }
                    }
                }
            }
        } else /* if (orig is a standard JavaBean) */ {
            PropertyDescriptor[] origDescriptors =
                getPropertyDescriptors(orig);
            for (int i = 0; i < origDescriptors.length; i++) {
                String name = origDescriptors[i].getName();
                if (isReadable(orig, name) && isWriteable(dest, name)) {
                    try {
                        Object value = getSimpleProperty(orig, name);
                        setSimpleProperty(dest, name, value);
                    } catch (NoSuchMethodException e) {
                        if (traceEnabled) {
                        	logger.log(Level.INFO,"Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);
                        }
                    }
                }
            }
        }

    }

	/**
	 * <p>Return <code>true</code> if the specified property name identifies
	 * a readable property on the specified bean; otherwise, return
	 * <code>false</code>.
	 *
	 * @param bean Bean to be examined
	 * @param name Property name to be evaluated
	 * @return <code>true</code> if the property is readable,
	 * otherwise <code>false</code>
	 *
	 * @exception IllegalArgumentException if <code>bean</code>
	 *  or <code>name</code> is <code>null</code>
	 *
	 */
	public boolean isReadable(Object bean, String name) {
	
	    // Validate method parameters
	    if (bean == null) {
	        throw new IllegalArgumentException("No bean specified");
	    }
	    if (name == null) {
	        throw new IllegalArgumentException("No name specified for bean class '" +
	                bean.getClass() + "'");
	    }
	
	    // Resolve nested references
	    while (resolver.hasNested(name)) {
	        String next = resolver.next(name);
	        Object nestedBean = null;
	        try {
	            nestedBean = getPropertyChecked(bean, next);
	        } catch (IllegalAccessException e) {
	            return false;
	        } catch (InvocationTargetException e) {
	            return false;
	        } catch (NoSuchMethodException e) {
	            return false;
	        }
	        if (nestedBean == null) {
	            throw new NestedNullException
	                    ("Null property value for '" + next +
	                    "' on bean class '" + bean.getClass() + "'");
	        }
	        bean = nestedBean;
	        name = resolver.remove(name);
	    }
	
	    // Remove any subscript from the final name value
	    name = resolver.getProperty(name);
	
	    try {
	        PropertyDescriptor desc =
	            getPropertyDescriptor(bean, name);
	        if (desc != null) {
	            Method readMethod = getReadMethod(bean.getClass(), desc);
	            if (readMethod == null) {
	                if (desc instanceof IndexedPropertyDescriptor) {
	                    readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod();
	                } else if (desc instanceof MappedPropertyDescriptor) {
	                    readMethod = ((MappedPropertyDescriptor) desc).getMappedReadMethod();
	                }
	                readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
	            }
	            return (readMethod != null);
	        } else {
	            return (false);
	        }
	    } catch (IllegalAccessException e) {
	        return (false);
	    } catch (InvocationTargetException e) {
	        return (false);
	    } catch (NoSuchMethodException e) {
	        return (false);
	    }
	
	
	}

	/**
	 * <p>Return <code>true</code> if the specified property name identifies
	 * a writeable property on the specified bean; otherwise, return
	 * <code>false</code>.
	 *
	 * @param bean Bean to be examined
	 * @param name Property name to be evaluated
	 * @return <code>true</code> if the property is writeable,
	 * otherwise <code>false</code>
	 *
	 * @exception IllegalArgumentException if <code>bean</code>
	 *  or <code>name</code> is <code>null</code>
	 *
	 */
	public boolean isWriteable(Object bean, String name) {
	
	    // Validate method parameters
	    if (bean == null) {
	        throw new IllegalArgumentException("No bean specified");
	    }
	    if (name == null) {
	        throw new IllegalArgumentException("No name specified for bean class '" +
	                bean.getClass() + "'");
	    }
	
	    // Resolve nested references
	    while (resolver.hasNested(name)) {
	        String next = resolver.next(name);
	        Object nestedBean = null;
	        try {
	            nestedBean = getPropertyChecked(bean, next);
	        } catch (IllegalAccessException e) {
	            return false;
	        } catch (InvocationTargetException e) {
	            return false;
	        } catch (NoSuchMethodException e) {
	            return false;
	        }
	        if (nestedBean == null) {
	            throw new NestedNullException
	                    ("Null property value for '" + next +
	                    "' on bean class '" + bean.getClass() + "'");
	        }
	        bean = nestedBean;
	        name = resolver.remove(name);
	    }
	
	    // Remove any subscript from the final name value
	    name = resolver.getProperty(name);
	
	
	    try {
	        PropertyDescriptor desc =
	            getPropertyDescriptor(bean, name);
	        if (desc != null) {
	            Method writeMethod = getWriteMethod(bean.getClass(), desc);
	            if (writeMethod == null) {
	                if (desc instanceof IndexedPropertyDescriptor) {
	                    writeMethod = ((IndexedPropertyDescriptor) desc).getIndexedWriteMethod();
	                } else if (desc instanceof MappedPropertyDescriptor) {
	                    writeMethod = ((MappedPropertyDescriptor) desc).getMappedWriteMethod();
	                }
	                writeMethod = MethodUtils.getAccessibleMethod(bean.getClass(), writeMethod);
	            }
	            return (writeMethod != null);
	        } else {
	            return (false);
	        }
	    } catch (IllegalAccessException e) {
	        return (false);
	    } catch (InvocationTargetException e) {
	        return (false);
	    } catch (NoSuchMethodException e) {
	        return (false);
	    }
	}

	public <T>BeanDifference createBeanDifference(final Function<T,Object> rightGetter){
		if(null == rightGetter){
			throw new NullPointerException("rightGetter is null");
		}
		return new BeanDifference(this) {
			@SuppressWarnings("unchecked")
			@Override
			protected Object left(Object right) {
				return rightGetter.apply((T) right);
			}
		};
	}
    /**
     * 以嵌套节点方式输出指定对象所有字段内容
     * @param bean Bean whose properties are to be extracted
     * @return The set of properties for the bean
     */
    public Map<String, Object> describe(Object bean){
    	return describe(bean,false);
    }
    /**
     * 以嵌套节点方式输出指定对象所有字段内容
     * @param bean Bean whose properties are to be extracted
     * @param throwOnCycleReference 当检测到循环引用时是否抛出{@link CycleReferenceException}
     * @return The set of properties for the bean
     * @since 2.7.7
     */
    public Map<String, Object> describe(Object bean,boolean throwOnCycleReference){
    	Map<String, Object> nodes = new LinkedHashMap<String, Object>();
    	NestedContext nestedContext = new NestedContext(bean, null, throwOnCycleReference);
    	nestedContext.describe(nodes);
    	return nodes;
    }
    /**
     * 检查是否有循环引用,如果有循环引用返回{@code true}否则返回{@code false}
     * @param bean
     * @since 2.7.7
     */
    public boolean hasCycleReference(Object bean) {
    	try {
    		checkCycleReference(bean);
			return false;
		} catch (CycleReferenceException e) {
			return true;
		}
    }
    /**
     * 检查是否有循环引用,有则抛出异常
     * @param bean
     * @throws CycleReferenceException 检测到循环引用
     * @since 2.7.7
     */
    public void checkCycleReference(Object bean) throws CycleReferenceException {
    	new NestedContext(bean, null, true).describe(null);
    }
    /**
     * 返回两个对象之间差异的字段
     * @param left
     * @param right
     */
	public Map<String,DiffNode> different(Object left,Object right){
		return different(left,right,null,null, true, new String[0]);
	}
    /**
	 * 返回两个对象之间差异的字段
	 * @param left
	 * @param right
	 */
	public Map<String,DiffNode> different(Object left,Object right,
			String focusNames){
		return different(left,right,Predicates.<String>alwaysTrue(),null, true, focusNames);
	}

	/**
     * 返回两个对象之间差异的字段
     * @param left
     * @param right
     */
	public Map<String,DiffNode> different(Object left,Object right,
			String... focusNames){
		return different(left,right,Predicates.<String>alwaysTrue(),null, true, focusNames);
	}
	/**
	 * 返回两个对象之间差异的字段
	 * @param left
	 * @param right
	 */
	public Map<String,DiffNode> different(Object left,Object right,
			Iterable<String>focusNames){
		return different(left,right,Predicates.<String>alwaysTrue(),null, focusNames, true);
	}
	/**
	 * 返回两个对象之间差异的字段
	 * @param left
	 * @param right
	 * @param originNameFilter
	 * @param focusNames
	 * @return
	 */
	public Map<String,DiffNode> different(Object left,Object right,Predicate<String> originNameFilter,
			Iterable<String>focusNames){
		return different(left,right,originNameFilter,null, focusNames, true);
	}
	/**
	 * 返回两个对象之间差异的字段
	 * @param left
	 * @param right
	 * @param originNameFilter
	 * @param focusNames
	 * @return
	 */
	public Map<String,DiffNode> different(Object left,Object right,Predicate<String> originNameFilter,
			String...focusNames){
		return different(left,right,originNameFilter,null, true,focusNames);
	}
	/**
	 * 返回两个对象之间差异的字段，返回不同的字段差异信息
	 * @see #different(Object, Object, Predicate, Iterable, Iterable, boolean)
	 */
	public Map<String,DiffNode> different(Object left,Object right,
			Predicate<String> originNameFilter,
			String[] forceExcludeNames, 
			boolean includeRequired, 
			String... focusNames){
		List<String> names = null == 
				focusNames ? Collections.<String>emptyList() : Arrays.asList(focusNames); 
		List<String> excludeNames = 
				null == forceExcludeNames ? Collections.<String>emptyList() : Arrays.asList(forceExcludeNames); 
		return different(left,right,originNameFilter,excludeNames, names, includeRequired);
	}
	/**
	 * 返回两个对象之间差异的字段，返回不同的字段差异信息
	 * @param left
	 * @param right
	 * @param originNameFilter
	 * @param forceExcludeNames 强制排除比较的字段名
	 * @param focusNames 比较字段黑/白名单
	 * @param includeRequired 为{@code true}时{@code focusNames}为白名单,否则为黑名单
	 */
	public Map<String,DiffNode> different(Object left,Object right,
			Predicate<String> originNameFilter,
			Iterable<String> forceExcludeNames, Iterable<String> focusNames, 
			boolean includeRequired){
        if(null == left || null == right){
        	HashMap<String,DiffNode> map = new HashMap<>();
        	map.put(".",new DiffNode(left, right));
        	return map;
        }
        /** 用于后续过滤字段的过滤器 */
        Predicate<String> nameFilter = new NestedNameFilter(originNameFilter,forceExcludeNames, focusNames, includeRequired);
        Map<String, DiffNode> diff = new LinkedHashMap<String, DiffNode>();
        different(left, right,diff,nameFilter);
        return diff;
    }
    /**
	 * 在{@code left}对象为基准，输出{@code left VS right}之间的差异到{@code diff}
	 * @param left
	 * @param right
	 * @param diff
	 * @param nameFilter
	 * @see #describe(Object)
	 */
	private void different(Object left,Object right,Map<String,DiffNode> diff, Predicate<String> nameFilter){
    	Map<String, Object> descLeft = describe(left);
    	Map<String, Object> descRight = describe(right);
    	for(Iterator<String> itor = Iterators.concat(descLeft.keySet().iterator(), descRight.keySet().iterator());itor.hasNext();){
    		String nestedName = itor.next();
    		if(!nameFilter.apply(nestedName) || containOrParent(diff.keySet(),nestedName)){
    			/** 名字过滤器不通过或父节点存在则跳过 */
    			continue;
    		}
    		if (nameFilter instanceof NestedNameFilter && !((NestedNameFilter)nameFilter).focusNames.isEmpty()) {
    			/** 指定focusNames时,只比较指定的字段 */
    			addIfNoEqual(diff,nestedName,descLeft.get(nestedName),descRight.get(nestedName));
			}else {
				Object lv = descLeft.get(nestedName);
				Object rv;
				if(descRight.containsKey(nestedName)){
					/** 左右俩侧都有同名的字段 */
					addIfNoEqual(diff,nestedName,lv,descRight.get(nestedName));
				}else if(null != (rv = getProperty(right, nestedName))){
					/** 右侧有值 */
					addIfNoEqual(diff,nestedName,lv,rv);
				}else {
					/** 右侧为null,向上查找右侧存在的父节点作比较 */
					String nk = nestedName;
					while(resolver.hasNested(nk)){
						nk = resolver.removeRight(nk);
						if(descRight.containsKey(nk)){
							break;
						}
					}
					addIfNoEqual(diff,nk,getProperty(left, nk),getProperty(right, nk));
				}
			}
    	}
    }
	/**
	 * 查找集合中是否存在指定的名字或嵌套节点名的父节点
	 * @param names
	 * @param nestName
	 */
	private boolean containOrParent(Set<String>names,String nestName){
		if(names.contains(nestName)){
			return true;
		}
		if(resolver.hasNested(nestName)){
			for(String name:names){
				if(nestName.startsWith(name)){
					return true;
				}
			}
		}
		return false;
	}

	private void addIfNoEqual(Map<String,DiffNode> diff,String name,Object lv,Object rv){
		if(!deepEquals(lv,rv)){
			diff.put(name,new DiffNode(
					differentValue(asAccessibleBean(lv)),
					differentValue(asAccessibleBean(rv))));	
		}
	}

	private Object asAccessibleBean(Object bean){
		if(maybeJsonString(bean) > 0){
			return jsonSupportInstance().parseOrEmptyMap((String)bean,false);
		}
		return bean;
	}

	private Object differentValue(Object input){
    	if(null == input){
    		return "null";
    	}else if(byte[].class.equals(input.getClass())){
    		return "[binary]" + Array.getLength(input) + "bytes";
    	}else if(input.getClass().isArray()){
    		return input.getClass().getComponentType().getSimpleName() + "["+Array.getLength(input)+"]";
    	}else if(input instanceof Iterable){
    		return input.getClass().getSimpleName() + "["+  Iterables.size((Iterable<?>) input) + "] ";
    	} else {
    		return input;
    	}
    }
    public void addPropertyDescriptorFactoryAsFirst(PropertyDescriptorFactory element) {
		propertyDescriptorFactories.addFirst(element);
	}
	public void addPropertyDescriptorFactory(PropertyDescriptorFactory element) {
		propertyDescriptorFactories.add(element);
	}
	/**
	 * 在{@code input}中遍历所有元素找到匹配的值,没找到返回{@code null}
	 * @param input
	 * @param field
	 * @param conditionValue
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	private Object searchIn(Object input,String field,Object conditionValue) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		if(input  instanceof Iterable) {
			for(Iterator<?> itor = ((Iterable<?>)input).iterator();itor.hasNext();){
				Object element = itor.next();
				if(String.valueOf(getProperty0(element, field)).equals(String.valueOf(conditionValue))){
					return element;
				}
			}
			return null;
		}else if(input instanceof Map){
			/** Map 的所有值中查找*/
			return searchIn(((Map<?, ?>)input).values(),field,conditionValue);
		}else if(null != input && input.getClass().isArray()){
			for(int i=0,end_i=Array.getLength(input);i<end_i;++i){
				Object element = Array.get(input, i);
				if(String.valueOf(getProperty0(element, field)).equals(String.valueOf(conditionValue))){
					return element;
				}
			}
			return null;
		}
		throw new IllegalArgumentException("Property '" + field +
				"' is not search on bean class '" + input.getClass() + "'");
	}
	/**
     * 当{@code nestedBean}为{@code null}时尝试为{@code bean}的{@code name}字段构造新实例
     * @param nestedBean
     * @param bean
     * @param name
     * @param expectType 期望的新实例类型，为{@code null}使用从字段对应的{@link PropertyDescriptor}实例中定义的类型
     * @return
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
    private Object tryConstructIfNull(Object nestedBean,Object bean, String name, Class<?> expectType) 
    		throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
    	if(null != nestedBean){
    		return nestedBean;
    	}
    	if(null == bean){
    		throw new NullPointerException("bean is null");
    	}
    	PropertyDescriptor descriptor;
    	try {
    		descriptor = getPropertyDescriptor(bean,name);
    		if(null == descriptor){
                throw new NoSuchMethodException("Unknown property '" +
                        name + "' on class '" + bean.getClass() + "'" );
            }
		} catch (ReflectiveOperationException e) {
			throw new BeanPropertyRuntimeException(e);
		}
		try {
			if(null == expectType){				
				expectType = descriptor.getPropertyType();
				if(List.class.equals(expectType)){
					expectType = ArrayList.class;
				}else if(Set.class.equals(expectType)){
					expectType = LinkedHashSet.class;
				}else if(Map.class.equals(expectType)){
					expectType = LinkedHashMap.class;
				}
			}
			Object newBean;
			if(String.class.equals(expectType)){
				/** String 类型时设置为空的JSON 字符串 */
				newBean = "{}";
			}else {
				newBean = expectType.getConstructor().newInstance();
			}
			setProperty0(bean, name, newBean);
			return newBean;
    	} catch (InstantiationException | IllegalArgumentException | SecurityException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
    		throw new NestedNullException
    		("Null property value for '" + name +
    				"' on bean class '" + bean.getClass() + "',AND FAIL to try construct new instance",e);
    	}
    }
	/** This just catches and wraps IllegalArgumentException. */
	private Object invokeMethod(
						PropertyDescriptor descriptor,
	                    Method method,
	                    Object bean,
	                    Object[] values)
	                        throws
	                            IllegalAccessException,
	                            InvocationTargetException {
		if(descriptor instanceof NoStandardPropertyDescriptor){
			InvokeMewthodContext context = new InvokeMewthodContext(descriptor, method, bean, values);
			((NoStandardPropertyDescriptor)descriptor).beforeInvokeMethod(context);
			return  invokeMethod(method, context.bean, context.values);
		}else{
        	// Call the property getter and return the value
        	return invokeMethod(method, bean, values);
		}
	}
	/** This just catches and wraps IllegalArgumentException. */
	private Object invokeMethod(
	                    Method method,
	                    Object bean,
	                    Object[] values)
	                        throws
	                            IllegalAccessException,
	                            InvocationTargetException {
	    if(bean == null && !NoStandardPropertyDescriptor.class.isAssignableFrom(method.getDeclaringClass())) {
	        throw new IllegalArgumentException("No bean specified " +
	            "- this should have been checked before reaching this method");
	    }
	
	    try {
	
	        return method.invoke(bean, values);
	
	    } catch (NullPointerException cause) {
	        // JDK 1.3 and JDK 1.4 throw NullPointerException if an argument is
	        // null for a primitive value (JDK 1.5+ throw IllegalArgumentException)
	        String valueString = "";
	        if (values != null) {
	            for (int i = 0; i < values.length; i++) {
	                if (i>0) {
	                    valueString += ", " ;
	                }
	                if (values[i] == null) {
	                    valueString += "<null>";
	                } else {
	                    valueString += (values[i]).getClass().getName();
	                }
	            }
	        }
	        String expectedString = "";
	        Class<?>[] parTypes = method.getParameterTypes();
	        if (parTypes != null) {
	            for (int i = 0; i < parTypes.length; i++) {
	                if (i > 0) {
	                    expectedString += ", ";
	                }
	                expectedString += parTypes[i].getName();
	            }
	        }
	        IllegalArgumentException e = new IllegalArgumentException(
	            "Cannot invoke " + method.getDeclaringClass().getName() + "."
	            + method.getName() + " on bean class '" + bean.getClass() +
	            "' - " + cause.getMessage()
	            // as per https://issues.apache.org/jira/browse/BEANUTILS-224
	            + " - had objects of type \"" + valueString
	            + "\" but expected signature \""
	            +   expectedString + "\"",
	            cause
	            );
	        
	        throw e;
	    } catch (IllegalArgumentException cause) {
	        String valueString = "";
	        if (values != null) {
	            for (int i = 0; i < values.length; i++) {
	                if (i>0) {
	                    valueString += ", " ;
	                }
	                if (values[i] == null) {
	                    valueString += "<null>";
	                } else {
	                    valueString += (values[i]).getClass().getName();
	                }
	            }
	        }
	        String expectedString = "";
	        Class<?>[] parTypes = method.getParameterTypes();
	        if (parTypes != null) {
	            for (int i = 0; i < parTypes.length; i++) {
	                if (i > 0) {
	                    expectedString += ", ";
	                }
	                expectedString += parTypes[i].getName();
	            }
	        }
	        IllegalArgumentException e = new IllegalArgumentException(
	            "Cannot invoke " + method.getDeclaringClass().getName() + "."
	            + method.getName() + " on bean class '" + bean.getClass() +
	            "' - " + cause.getMessage()
	            // as per https://issues.apache.org/jira/browse/BEANUTILS-224
	            + " - had objects of type \"" + valueString
	            + "\" but expected signature \""
	            +   expectedString + "\"",
	            cause
	            );
	        throw e;
	
	    }
	}

	private static boolean isBaseType(Object object){
		if(null == object){
			return true;
		}
		Class<? extends Object> clazz = object.getClass();
		if(Primitives.unwrap(clazz).isPrimitive()){
			return true;
		}
		if(object instanceof String){
			return true;
		}
		if(object instanceof Date){
			return true;
		}
		if(clazz.isArray()){
			return Primitives.unwrap(clazz.getComponentType()).isPrimitive();
		}
		return false;
	}

	private static boolean traceEnabled = false;
	public static void setTraceEnabled(boolean traceEnabled) {
		BeanPropertySupport.traceEnabled = traceEnabled;
		MethodUtils.setTraceEnabled(traceEnabled);
	}
	/**
	 * 判断输入参数是否为{@code null}或空<br>
	 * 如果输入参数为@{@link String},{@link Collection},{@link Iterable},{@link Iterator},{@link Map},数组类型则返回其是否为空,
	 * 否则返回{@code false}
	 * @param value 为{@code null}返回{@code true}
	 */
	public static boolean isEmpty(Object value){
		if(null == value){
			return true;
		}else	if(value instanceof String){
			return ((String)value).isEmpty();
		}else if (value instanceof Collection) {
			return ((Collection<?>)value).isEmpty();
		}else if (value instanceof Iterator) {
			return !((Iterator<?>)value).hasNext();
		}else if (value instanceof Iterable) {
			return !((Iterable<?>)value).iterator().hasNext();
		}else if (value instanceof Enumeration) {
			return !((Enumeration<?>)value).hasMoreElements();
		}else if (value instanceof Map) {
			return ((Map<?, ?>)value).isEmpty();
		}else if (value.getClass().isArray()) {
			return Array.getLength(value)==0;
		}
		return false;
	}
	private static boolean hasReadMethod(PropertyDescriptor propertyDescriptor) {
		if(null != propertyDescriptor){
			Method m = propertyDescriptor.getReadMethod();
			if (m != null) {
				return true;
			}			
		}
		return false;
	}
	private static boolean hasWriteMethod(PropertyDescriptor propertyDescriptor) {
		if(null != propertyDescriptor){
			Method m = propertyDescriptor.getWriteMethod();
			if (m != null) {
				return true;
			}
		}
		return false;
	}
	/**
	 * 判断对象是否有可能是JSON 字符串,
	 * 如果是String类型且前后以{}返回1
	 * 如果是String类型且前后以[]返回2
	 * 否则返回0
	 * @param object
	 */
	private static int maybeJsonString(Object object){
		if(object instanceof String){
			String s = (String)object;
			if(s.length() > 1){
				if(s.charAt(0) == '{' && s.charAt(s.length()-1)=='}'){
					return 1;
				}
				if(s.charAt(0) == '[' && s.charAt(s.length()-1)==']'){
					return 2;
				}
			}
		}
		return 0;
	}
	public static class DiffNode{
		Object left;
		Object right;
		public DiffNode() {
			super();
		}
		public DiffNode(Object left, Object right) {
			this.left = left;
			this.right = right;
		}
		public Object getLeft() {
			return left;
		}
		public void setLeft(Object left) {
			this.left = left;
		}
		public Object getRight() {
			return right;
		}
		public void setRight(Object right) {
			this.right = right;
		}
		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("DiffNode [left=");
			builder.append(left);
			builder.append(", right=");
			builder.append(right);
			builder.append("]");
			return builder.toString();
		}
		
	}
	class NestedContext{
		Object bean;
		/**
		 * 要访问的字段名
		 */
		String name;
		/**
		 * 当{@link #bean}为JSON 字符串时转换的JSON对象
		 */
		Object convertObject;
		/**
		 * 字段值
		 */
		Object value;
		/**
		 * 父节点
		 */
		NestedContext parent;
		/**
		 * 当检测到循环引用时是否抛出{@link CycleReferenceException}
		 */
		boolean throwOnCycleReference;
		private NestedContext(NestedContext parent, String name, Object value) {
			this.parent = parent;
			this.name = name;
			this.value = value;
			this.throwOnCycleReference = parent.throwOnCycleReference;
		}
		private NestedContext(NestedContext parent, Object bean) {
			super();
			this.bean = bean;
			this.parent = parent;
			this.throwOnCycleReference = parent.throwOnCycleReference;
		}
		protected NestedContext(Object bean, String name, boolean throwOnCycleReference) {
			this.bean = bean;
			this.name = name;
			this.throwOnCycleReference = throwOnCycleReference;
		}
		protected NestedContext(Object bean, String name, Object value) {
			this.bean = bean;
			this.name = name;
			this.value = value;
		}
		private void normailzeBean(boolean throwOnfail){
			if(null == convertObject && maybeJsonString(this.bean) > 0){
    			this.convertObject = jsonSupportInstance().parseOrEmptyMap((String)this.bean,throwOnfail);
    		}
		}
		Object getAccessibleBean(){
			return null == convertObject ? bean : convertObject;
		}
		/**
	     * [递归]解析嵌套节点
	     * @param visitor
	     * @throws IllegalAccessException
	     * @throws InvocationTargetException
	     * @throws NoSuchMethodException
	     */
	    Object visit(NestedNodeVisitor visitor)
	    		throws IllegalAccessException, InvocationTargetException,
	    		NoSuchMethodException {
	    	if(visitor.beforeVisit(this)){
	    		return this.bean;
	    	}
	    	
	    	// Resolve nested references
	    	if (resolver.hasNested(this.name)) {
	    		String propertyName = resolver.next(this.name);
	    		NestedContext nextContext = new NestedContext(this, this.name, this.value);
	    		nextContext.name = resolver.remove(this.name);
	    		normailzeBean(true);
	    		if (resolver.isMapped(propertyName)) {
	    			nextContext.bean = getMappedProperty(getAccessibleBean(), propertyName);
	    		} else if (resolver.isIndexed(propertyName)) {
	    			nextContext.bean = getIndexedProperty(getAccessibleBean(), propertyName);
	    		} else if (resolver.isSearched(propertyName)) {
	    			nextContext.bean = getSearchedProperty(getAccessibleBean(), propertyName);
	    		} else {
	    			nextContext.bean = getSimpleProperty(getAccessibleBean(), propertyName);
	    		}
	    		if(visitor.beofreNext(nextContext)){
	    			return nextContext.bean;
	    		}
	    		// 进入次级节点
	    		Object vret = nextContext.visit(visitor);
	    		if(visitor.afterNext(nextContext)){
	    			return this.bean;
	    		}
	    		return vret;
	    	}
	    	normailzeBean(true);
	    	if(visitor.lastNode(this)){
	    		return this.value;
	    	}
	    	return  null;
	    }
	    
	    /**
	     * 检查子节点是否存在循环引用
	     * @param child
	     * @return 存在循环引用返回{@code true}否则返回{@code false}
	     */
	    private boolean checkCycleRef(Object child) {
	    	for(NestedContext context = this; null != context; context = context.parent) {
	    		if(child == context.bean) {
	    			return true;
	    		}
	    	}
	    	return false;
	    }
	    /**
	     * 计算节点的全路径
	     */
	    private String nestedName() {
			List<String>nestedNames = new ArrayList<>(16);
			for (NestedContext cx = this; null != cx && null != cx.name; cx = cx.parent) {
				nestedNames.add(cx.name);
			}
			Collections.reverse(nestedNames);
			StringBuilder builder = new StringBuilder();
			for(int i=0;i<nestedNames.size();++i) {
				String name = nestedNames.get(i);
				if(i > 0 && !name.startsWith("[")) {
					builder.append('.');
				}
				builder.append(name);
			}
			return builder.toString();
	    }
	    /**
	     * 递归解析所有子节点以平面方式返回所有嵌套节点的值
	     * @param nodes
	     * @return 当前节点的子节点数量
	     */
		int describe(Map<String, Object> nodes){
	    	normailzeBean(false);
	    	List<String> names = getNames();
	    	if(names.isEmpty() && null == parent){
	    		if(null != nodes) {
	    			nodes.put(".", this.bean);
	    		}
	    	}
	    	for(String name : names){
	    		try {
	    			this.name = name;
	    			Object child = readValue();
	    	    	/** 循环引用则抛出异常 */
	    			if(checkCycleRef(child)) {
	    				if(throwOnCycleReference) {
	    					throw new CycleReferenceException(nestedName(),String.valueOf(child));
	    				}else {
	    					/** 叶子节点 */
	    					if(null != nodes) {
	    						nodes.put(nestedName(), "<CycleReferenceException>");
	    					}
	    				}
	    			}else {
	    				NestedContext nestedContext = new NestedContext(this, child);
	    				// 进入次级节点
	    				int cnt = nestedContext.describe(nodes);
	    				if(0 == cnt){
	    					if(null != nodes) {
	    						/** 叶子节点 */
	    						nodes.put(nestedName(), nestedContext.bean);
	    					}
	    				}
	    			}
	    		} finally{
	    			this.name = null;
	    		}
	    	}
	    	return names.size();
	    }
		/**
		 * 返回当前节点对象({@link #bean}或{@link #convertObject})所有的可读取字段名,
		 * 如果对象为{@link Map}返回所有的key,
		 * 对象为数组或{@ List}时使用'[x]'作为元素名,
		 * 对象为{@code null}返回空表
		 */
		@SuppressWarnings("rawtypes")
		private List<String> getNames(){
	    	ArrayList<String> names = new ArrayList<>(16);
	    	Object accessibleBean = getAccessibleBean();
			if(null == accessibleBean) {
	    		// DO NOTHING
	    	}else if(isBaseType(accessibleBean)){
	    		// DO NOTHING
	    	}else if(accessibleBean instanceof Map){
	    		for(Object key:((Map<?, ?>)accessibleBean).keySet()){
	    			if(key instanceof String){
	    				names.add((String)key);
	    			}
	    		}
	    	}else if(accessibleBean instanceof List) {
	    		/** List使用'[x]'作为元素名 */
	    		for(int i=0,elementCount = ((List)accessibleBean).size() ; i < elementCount; ++i) {
	    			names.add( "[" + i + "]" );
	    		}
	    	}else if(accessibleBean.getClass().isArray()) {
	    		/** 数组使用'[x]'作为元素名 */
	    		for(int i=0,elementCount = Array.getLength(accessibleBean) ; i < elementCount; ++i) {
	    			names.add( "[" + i + "]" );
	    		}
	    	}else{
	    		Map<String, PropertyDescriptor> descriptors = getMappedPropertyDescriptors(bean);
	        	for(Entry<String, PropertyDescriptor> entry : descriptors.entrySet()){
	        		if (hasReadMethod(entry.getValue())) {
	        			names.add(entry.getKey());
	        		}
	        	}
			}
	    	return names;
	    }
	    Object readValueChecked() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
	    	if(null != name){
	    		normailzeBean(false);
	    		Object accessibleBean = getAccessibleBean();
	    		if(null == accessibleBean){
	    			return null;
	    		}
	    		String next = resolver.hasNested(name) ? resolver.next(name):name;
	    		return getProperty0(accessibleBean,next);
	    		
	    	}else {
	    		// 顶级节点
				return bean;
			}
	    }
	    Object readValue() {
	    	try {
				return readValueChecked();
			} catch (NoSuchMethodException e) {
				return null;
			} catch (ReflectiveOperationException e) {
				throw new RuntimeException(e);
			}
	    }
	}
	/**
	 * 读访问接口实现
	 * @author guyadong
	 *
	 */
	protected class ReadVisitor implements NestedNodeVisitor {

		@Override
		public boolean beforeVisit(NestedContext context) {
			return (context.bean == null || context.name == null);
		}

		@Override
		public boolean beofreNext(NestedContext context) {
			return null == context.bean;
		}

		@Override
		public boolean afterNext(NestedContext context)
				throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
					return false;
		}

		@Override
		public boolean lastNode(NestedContext context) throws 
				InvocationTargetException, NoSuchMethodException, IllegalAccessException {
			context.value = getProperty0(context.getAccessibleBean(),context.name);
			return true;
		}

	}
	/**
	 * 写访问接口实现
	 * @author guyadong
	 *
	 */
	protected class WriteVisitor implements NestedNodeVisitor {
		
		@Override
		public boolean beforeVisit(NestedContext context) {
	        if (context.bean == null) {
	            throw new IllegalArgumentException("No bean specified");
	        }
	        if (context.name == null) {
	            throw new IllegalArgumentException("No name specified for bean class '" +
	            		context.bean.getClass() + "'");
	        }
	        return false;
		}
		
		@Override
		public boolean beofreNext(NestedContext context) 
				throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
			String next = resolver.next(context.parent.name);
			Class<?> expectType = null;
			Object accessibleBean = context.parent.getAccessibleBean();
			if (accessibleBean instanceof Map ) {
                expectType = LinkedHashMap.class;
            	if(resolver.isIndexed(next)){
            		expectType = LinkedList.class;
            	}
            }
			context.bean = tryConstructIfNull(context.bean, accessibleBean,next, expectType);
			return false;
		}
		
		@Override
		public boolean afterNext(NestedContext context)
				throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
			if(maybeJsonString(context.bean) > 0){
				/** 当前Bean对象类型为String时,将JSON对象序列化为String保存将由上一级处理 */
				context.bean = jsonSupportInstance().toJSONString(context.convertObject);
				/** 上一级设置下级返回的JSON字符串字段 */
				setProperty0(context.parent.bean,resolver.next(context.parent.name),context.bean);
				
				return true;
			}else if(maybeJsonString(context.parent.bean) > 0){
				/** 当前Bean对象类型为String时*/
				context.parent.bean = jsonSupportInstance().toJSONString(context.parent.convertObject);
				return true;
			}
			return false;
		}

		@Override
		public boolean lastNode(NestedContext context) throws 
				InvocationTargetException, NoSuchMethodException, IllegalAccessException {
			
			if(context.checkCycleRef(context.value)) {
				throw new CycleReferenceException(context.name,String.valueOf(context.value));
			}
			
			setProperty0(context.getAccessibleBean(),context.name,context.value);
			return false;
		}
		
	}
	private static class NestedNameFilter implements Predicate<String>{
		/** 定义需要比较的字段名 */
		final Set<String> focusNames;
		final Predicate<String> nameFilter;
		final Set<String> forceExcludeNames;
		final boolean includeRequired;
		private Set<String> normalize(Iterable<String> input){
			if(null != input ){
				/** 过滤出非null的字段名集 */
				Iterable<String> filtered = Iterables.filter(input,Predicates.notNull());
				/** 统一转成驼峰(camel-case)命名格式,以方便后续比较 */
				Iterable<String> cameCaseNames = Iterables.transform(filtered, 
						new Function<String,String>(){

					@Override
					public String apply(String input) {
						return toCamelcase(input);
					}});
				return Sets.newHashSet(cameCaseNames);
			}else {
				return Collections.emptySet();
			}
		}
		/**
		 * @param originNameFilter
		 * @param forceExcludeNames 强制排除比较的字段名
		 * @param focusNames 比较字段黑/白名单
		 * @param includeRequired 为{@code true}时{@code focusNames}为白名单,否则为黑名单
		 */
		NestedNameFilter(Predicate<String> originNameFilter,
				Iterable<String> forceExcludeNames, 
				Iterable<String> focusNames, 
				boolean includeRequired) {
			this.nameFilter = MoreObjects.firstNonNull(originNameFilter, Predicates.<String>alwaysTrue());
			this.focusNames= normalize(focusNames);
			this.forceExcludeNames= normalize(forceExcludeNames);
			this.includeRequired = includeRequired;
		}
		@Override
		public boolean apply(String input) {
			boolean allowed = false;
			if(null != input){
				/** 自动实现 snake-case 到 camel-case 的转换*/
				if(isSnakecase(input)){
					input = toCamelcase(input);
				}
				/** 强制排除的字段不参与比较 */
				if(forceExcludeNames.contains(input)){
					return false;
				}
				if(focusNames.isEmpty()){
					/** 为空时所有字段都在比较范围之内 */
					allowed = true;
				}else if(focusNames.contains(input)){
					allowed = true;
				}else {
					for(String name : focusNames){
						if(input.length() > name.length()){
							if(input.startsWith(name)){
								allowed = true;
								break;
							}
						} else if(name.length() > input.length()){
							if(name.startsWith(input)){
								/** 允许遍历父节点  */
								allowed = true;
								break;
							}
						}else if(name.equals(input)){
							/** DEAD CODE,不会走到这里 */
							allowed = true;
							break;
						}
					}
				}
			}
			if(includeRequired){
				return allowed ? nameFilter.apply(input) : false;
			}else {
				return allowed ? !nameFilter.apply(input) : false;
			}
		}
		
	}
}
